// imageStorage.cpp
//
// Image class storage

#include "image/imageStorage.h"
#include "image/../exception.h"

namespace simple{
    //
    // ImageStorageBase
    //
    ImageStorageBase::ImageStorageBase () {}

    ImageStorageBase::ImageStorageBase (const Rect& boundary) : _boundary (boundary) {}

    ImageStorageBase::~ImageStorageBase () {}

    //
    // RectImageStorage
    //
    RectImageStorage::RectImageStorage ()
        : _begin (0), _end (0), _xoffset (0), _yoffset (0),
          _bytesPerPixel (0), _rowSpacing (0), _align (eNoAlign) {}

    RectImageStorage::RectImageStorage (const Rect& boundary,
                                        unsigned int bytesPerPixel,
                                        eAlignment align)
        : ImageStorageBase (boundary), _begin (0), _end (0), 
          _xoffset (0), _yoffset (0), _bytesPerPixel (bytesPerPixel), 
          _rowSpacing (0), _align (align)
    {

        // Round up our row size for alignment purposes. The
        // enumeration values match our alignment values so we can 
        // cast it and do some bit manipulation.
        _rowSpacing = width () * _bytesPerPixel;
        if (_align != eNoAlign) {
            int alignment = static_cast<int>(_align) - 1;
            _rowSpacing = (_rowSpacing + alignment) & (~alignment);
        }

        // Allocate our memory. Force our base address to start at a
        // 8-byte aligned boundary.
        _storage = Alloc<Pel8> (_rowSpacing * boundary.height (), 8);
        _begin = _rowAddress (y0());
        _end   = _begin + height() * _rowSpacing;
    }

    RectImageStorage::~RectImageStorage () {}

    RectImageStorage::RectImageStorage (const RectImageStorage& src)
        : ImageStorageBase (src)
    {
        // We only need to hold on to the local locks
        RectImageStorageLocker srcLocking (src); 

        // Copy our base data again because we now have a lock
        _boundary      = src._boundary;

        _storage       = src._storage;
        _begin         = src._begin;
        _end           = src._end;
        _align         = src._align;
        _yoffset       = src._yoffset;
        _xoffset       = src._xoffset;
        _bytesPerPixel = src._bytesPerPixel;
        _rowSpacing    = src._rowSpacing;
    }

    RectImageStorage& RectImageStorage::operator= (const RectImageStorage& src)
    {
        // We only need to hold on to the local locks
        RectImageStorageLocker locking (*this);

        if (this != &src) {
            RectImageStorageLocker srcLocking (src); 
            // Don't forget our base class
            _boundary      = src._boundary;

            _storage       = src._storage;
            _begin         = src._begin;
            _end           = src._end;
            _align         = src._align;
            _yoffset       = src._yoffset;
            _xoffset       = src._xoffset;
            _bytesPerPixel = src._bytesPerPixel;
            _rowSpacing    = src._rowSpacing;
        }

        return *this;
    }


    bool        RectImageStorage::window (const Rect& window)
    {
        RectImageStorageLocker locking (*this);

        Rect intersect = boundary().intersect (window);
        if (intersect.isNull()) {
            // No intersection so make this a null image
            init ();
            return false;
        }

        // Adjust our boundary and compute the new offset to our
        // first pixel.
        // intersect.x0() - x0()相当于是边界的平移量，然后用边界平移量来修正offset
        _xoffset  += intersect.x0() - x0(); 
        _yoffset  += intersect.y0() - y0();
        _boundary  = intersect;
        _begin     = _rowAddress (y0());
        _end       = _begin + height() * _rowSpacing;

        return true;
    }



    void        RectImageStorage::init ()
    {
        _boundary      = Rect ();
        _xoffset       = 0;
        _yoffset       = 0;
        _bytesPerPixel = 0;
        _rowSpacing    = 0;
        _storage       = Alloc<Pel8>();
        _begin         = 0;
        _end           = 0;
    }


    Pel8*       RectImageStorage::_rowAddress (int y)
    {
        if (y < y0() || y >= y1())
            throw BoundsException ("RectImageStorage::_rowAddress");

//        y -= y0();
//        int offset = (y + _yoffset) * _rowSpacing // y + _yoffset = 原来的y + (_yoffset - y0)
//                      + _xoffset * _bytesPerPixel;
        int offset = (y + _yoffset - y0()) * _rowSpacing
                      + _xoffset * _bytesPerPixel;
        Pel8* p = _storage.data() + offset;
        return p;
    }

    // This is better than duplicating code
    const Pel8* RectImageStorage::_rowAddress (int y) const
    {
        return const_cast<RectImageStorage*> (this)->_rowAddress (y);
    }

    Pel8*       RectImageStorage::getAddr   (int x, int y)
    { 
        if (x < x0() || x >= x1())
            throw BoundsException ("RectImageStorage::getAddr");
        return _rowAddress (y) + (x-x0()) * _bytesPerPixel;
    }

    const Pel8* RectImageStorage::getAddr   (int x, int y) const
    {
        return const_cast<RectImageStorage*> (this)->getAddr (x, y);
    }

    const Pel8* RectImageStorage::getPixel  (int x, int y) const
    {
        return getAddr (x, y);
    }

    void        RectImageStorage::setPixel  (int x, int y, const Pel8* pixel)
    {
        Pel8* addr = getAddr (x, y);
        memcpy (addr, pixel, _bytesPerPixel);
    }


    void        RectImageStorage::rebase    ()
    {
        // Make sure we have our own copy if we are sharing the buffer
        _storage.duplicate ();

        // Change the origin point
        _boundary = Rect (0, 0, width(), height());
        _begin    = _rowAddress (y0());
        _end      = _begin + height() * _rowSpacing;
    }

}